﻿using System;
using System.Collections.Generic;
using EnvDTE;
using EnvDTE80;

namespace SolutionNavigateAddIn
{
    public static class UIHierarchyManager
    {

        static Dictionary<UIHierarchyObject, UIHierarchyItem> uihObjects = new Dictionary<UIHierarchyObject, UIHierarchyItem>();
        static Queue<UIHierarchyItem> queueProcessing = new Queue<UIHierarchyItem>();
        private static bool processingComplete = false;
        
        static UIHierarchyManager()
        {
            SnEvents.SolutionStateChanged += (SnEvents_SolutionStateChanged);
            SnEvents.ProjectChanged += (SnEvents_ProjectChanged);
        }

        static void SnEvents_ProjectChanged(object sender, SnEvents.ProjectEventArgs e)
        {
            Clean();
        }

        static void SnEvents_SolutionStateChanged(object sender, SnEvents.SolutionEventArgs e)
        {
            if (e.State == SnEvents.SolutionEventArgs.SolutionState.Open)
            {
                Enabled();
            }
            else
            {
                Clean();
            }
        }

        private static void Enabled()
        { }

        private static void Clean()
        {
            uihObjects.Clear();
            queueProcessing.Clear();
            processingComplete = false;
        }

        public static UIHierarchyItem GetUI(object @object)
        {
            UIHierarchyObject objectType = Converter2ObjectType(@object);

            if (objectType != null)
            {
                UIHierarchyItem value;
                if (uihObjects.TryGetValue(objectType, out value))
                    return value;

                do
                {
                    while (queueProcessing.Count > 0)
                    {
                        UIHierarchyItem item = queueProcessing.Dequeue();

                        UIHierarchyObject current = Converter2ObjectType(item.Object);
                        
                        if (current != null)
                        {
                            if (current.ObjectType == UIHierarchyObjectType.Project)
                            {
                                uihObjects[current]= item;
                            }
                        }

                        if (objectType.Equals(current))
                            return item;

                        if (IsNeedProcessing(item.Object))
                        {
                            foreach (UIHierarchyItem hierarchyItem in
                                    UIHierarchyItemsExtension.GetChildrenList(item.UIHierarchyItems))
                            {
                                queueProcessing.Enqueue(hierarchyItem);
                            }
                        }
                    }

                    if (!processingComplete)
                    {
                        foreach (UIHierarchyItem item in 
                            UIHierarchyItemsExtension.GetChildrenList(SnEnvironment.MainDTE.ToolWindows.SolutionExplorer.UIHierarchyItems))
                        {
                            queueProcessing.Enqueue(item);
                        }
                        processingComplete = true;
                    }

                } while (queueProcessing.Count > 0);
            }

            return null;
        }

        private static bool IsNeedProcessing(object @object)
        {
            return @object is Solution
                   ||
                   @object is SolutionFolder
                   ||
                   (@object is Project && ((Project)@object).Kind == WatcherConstant.SolutionFolder)
                   ||
                   (@object is ProjectItem && ((ProjectItem)@object).SubProject != null && ((ProjectItem)@object).SubProject.Kind == WatcherConstant.SolutionFolder);
        }

        private static UIHierarchyObject Converter2ObjectType(object @object)
        {
            if (@object is Solution)
                return new UIHierarchyObject(UIHierarchyObjectType.Solution, ((Solution) @object).FullName);

            if (@object is Project)
            {
                if (IsSolutionFolder((Project) @object))
                {
                    return new UIHierarchyObject(UIHierarchyObjectType.SolutionFolder, ((Project) @object).Name);
                }
                
                if (IsProject((Project)@object))
                {
                    return new UIHierarchyObject(UIHierarchyObjectType.Project, ((Project)@object).FullName);
                }

               /* if (IsUnloadProject((Project)@object))
                {
                    return new UIHierarchyObject(UIHierarchyObjectType.Project, ((Project) @object).UniqueName);
                }*/
            }

            if (@object is ProjectItem && ((ProjectItem)@object).Kind == WatcherConstant.SolutionFolder)
                return new UIHierarchyObject(UIHierarchyObjectType.SolutionFolder, ((ProjectItem)@object).Name);

            if (@object is ProjectItem 
                && 
                ((ProjectItem)@object).SubProject != null
                &&
                IsProject(((ProjectItem)@object).SubProject))
                return new UIHierarchyObject(UIHierarchyObjectType.Project, ((ProjectItem)@object).SubProject.FullName);

            return null;
        }

        private static bool IsUnloadProject(Project project)
        {
            return project.Kind == WatcherConstant.UnloadProject;
        }

        private static bool IsSolutionFolder(Project project)
        {
            return project.Kind == WatcherConstant.SolutionFolder;
        }

        private static bool IsProject(Project project)
        {
            switch (project.Kind)
            {
                case WatcherConstant.ProjectCSharp:
                case WatcherConstant.ProjectVB:
                case WatcherConstant.ProjectCPP:
                case WatcherConstant.ProjectJSharp:
                case WatcherConstant.ProjectWeb:
                    {
                        return true;
                    }
                default:
                    {
                        return false;
                    }
            }
        }

        private class UIHierarchyObject
        {
            public UIHierarchyObjectType ObjectType { get; private set; }
            public string FullName { get; private set; }

            public UIHierarchyObject(UIHierarchyObjectType objectType, string fullName)
            {
                ObjectType = objectType;
                FullName = fullName;
            }

            public override bool Equals(object obj)
            {
                return (obj != null
                        &&
                        obj is UIHierarchyObject
                        &&
                        ((UIHierarchyObject) obj).FullName == FullName
                        &&
                        ((UIHierarchyObject) obj).ObjectType == ObjectType);
            }

            public override int GetHashCode()
            {
                if (FullName != null)
                {
                    return ObjectType.GetHashCode() ^ FullName.GetHashCode();
                }

                return ObjectType.GetHashCode();
            }
        }

        /// <summary>
        /// Enum of object which represented of UIHierarchy
        /// </summary>
        private enum UIHierarchyObjectType
        {
            Solution,
            SolutionFolder,
            Project,
        }

    }
}